package com.tanhua.server.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanhua.common.enums.AgeRangeEnum;
import com.tanhua.common.enums.AreaEnum;
import com.tanhua.common.exception.BusinessException;
import com.tanhua.common.mapper.UserInfoMapper;
import com.tanhua.common.pojo.GenderDistribution;
import com.tanhua.common.pojo.IndustryDistribution;
import com.tanhua.common.pojo.LocalDistribution;
import com.tanhua.common.pojo.UserInfo;
import com.tanhua.common.vo.ErrorResult;
import com.tanhua.dubbo.server.api.LogApi;
import com.tanhua.dubbo.server.pojo.Log;
import com.tanhua.server.vo.DistributionVo;
import com.tanhua.server.vo.SummaryVo;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class DashboardService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Reference
    private LogApi logApi;


    /**
     * 查询注册用户分布，行业top、年龄、性别、地区
     *
     * @param sd 开始时间
     * @param ed 结束时间
     * @return
     */
    public Map<String, Object> queryDistribution(Long sd, Long ed) {
        //转换查询条件格式
        String dateTime1 = DateUtil.format(new Date(sd), "yyyy/MM/dd");
        String dateTime2 = DateUtil.format(new Date(ed), "yyyy/MM/dd");
        Map<String, Object> resultMap = new HashMap<>();
        //查询职业分布
        List<IndustryDistribution> industryDistribution = userInfoMapper.findIndustryDistribution(dateTime1, dateTime2);
        resultMap.put("industryDistribution", industryDistribution);
        //查询性别分布
        List<DistributionVo> sexList = new ArrayList<>();
        List<GenderDistribution> genderDistribution = userInfoMapper.findGenderDistribution(dateTime1, dateTime2);
        for (GenderDistribution distribution : genderDistribution) {
            DistributionVo distributionVo = new DistributionVo();
            distributionVo.setTitle(distribution.getTitle().getValue() == 1 ? "男性用户" : "女性用户");
            distributionVo.setAmount(distribution.getAmount());
            sexList.add(distributionVo);
        }
        resultMap.put("genderDistribution", sexList);
        //查询地区分布
        List<DistributionVo> localList = new ArrayList<>();
        List<LocalDistribution> localDistribution = userInfoMapper.findLocalDistribution(dateTime1, dateTime2);
        for (LocalDistribution distribution : localDistribution) {
            DistributionVo distributionVo = new DistributionVo();
            if (distribution.getTitle() == null) {
                continue;
            }
            distributionVo.setTitle(distribution.getTitle().getValue());
            distributionVo.setAmount(distribution.getAmount());
            localList.add(distributionVo);
        }
        //地域分布集合
        List<DistributionVo> localTotalList = queryLocalTotalList(localDistribution);

        List<DistributionVo> ageDistributionVoList = queryAgeDistributionVoList(sd, ed);

        resultMap.put("localDistribution", localList);
        resultMap.put("ageDistribution", ageDistributionVoList);
        resultMap.put("localTotal", localTotalList);

        return resultMap;
    }

    /**
     * 查询地域分布
     * @param localList
     * @return
     */
    private List<DistributionVo> queryLocalTotalList(List<LocalDistribution> localList) {

        List<DistributionVo> localTotalList = new ArrayList<>();
        //遍历城市分布
        List<Object> areaList = EnumUtil.getFieldValues(AreaEnum.class, "area");
        //去重复
        Set<String> areaSet = new HashSet<>();
        for (Object area : areaList) {
            areaSet.add(StrUtil.toString(area));
        }
        for (String area : areaSet) {
            Integer count = 0;
            for (LocalDistribution localDistribution : localList) {
                if (localDistribution.getTitle() == null) {
                    continue;
                }
                if (StrUtil.equals(area, localDistribution.getTitle().getArea())) {
                    count = count + localDistribution.getAmount();
                }
            }
            DistributionVo distributionVo = new DistributionVo(area, count);
            localTotalList.add(distributionVo);
        }

        return localTotalList;

    }

    /**
     * 查询用户年龄段分布
     *
     * @return
     */
    private List<DistributionVo> queryAgeDistributionVoList(Long sd, Long ed) {
        //转换查询条件格式
        String dateTime1 = DateUtil.format(new Date(sd), "yyyy/MM/dd");
        String dateTime2 = DateUtil.format(new Date(ed), "yyyy/MM/dd");
        //查询所有用户信息
        QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
        wrapper.between("created", dateTime1, dateTime2);
        List<UserInfo> userInfoList = userInfoMapper.selectList(wrapper);
        //定义多个变量,用来记录不同时间段用户数量
        int a = 0;
        int b = 0;
        int c = 0;
        int d = 0;
        int e = 0;
        for (UserInfo userInfo : userInfoList) {
            if (userInfo.getAge() > 0 && userInfo.getAge() < 20) {
                a++;
            }
            if (userInfo.getAge() > 20 && userInfo.getAge() < 30) {
                b++;
            }
            if (userInfo.getAge() > 30 && userInfo.getAge() < 40) {
                c++;
            }
            if (userInfo.getAge() > 40 && userInfo.getAge() < 50) {
                d++;
            }
            if (userInfo.getAge() > 50 && userInfo.getAge() < 200) {
                e++;
            }
        }
        //创建对象封装数据
        List<DistributionVo> ageDistributionVoList = new ArrayList<>();
        DistributionVo distributionVo1 = new DistributionVo(AgeRangeEnum.UNDER_TWENTY.getDesc(), a);
        DistributionVo distributionVo2 = new DistributionVo(AgeRangeEnum.TWENTY.getDesc(), b);
        DistributionVo distributionVo3 = new DistributionVo(AgeRangeEnum.THIRTY.getDesc(), c);
        DistributionVo distributionVo4 = new DistributionVo(AgeRangeEnum.FORTY.getDesc(), d);
        DistributionVo distributionVo5 = new DistributionVo(AgeRangeEnum.OVER_FIFTY.getDesc(), e);
        ageDistributionVoList.add(distributionVo1);
        ageDistributionVoList.add(distributionVo2);
        ageDistributionVoList.add(distributionVo3);
        ageDistributionVoList.add(distributionVo4);
        ageDistributionVoList.add(distributionVo5);
        return ageDistributionVoList;
    }

    /**
     * 新增、活跃用户、次日留存率
     *
     * @param type 类型
     * @param sd   开始时间
     * @param ed   结束时间
     * @return
     */
    public Map<String, Object> queryUsers(String type, Long sd, Long ed) {
        if (type.equals("101")) {
            //查询新增用户
            return queryCrescent(sd, ed);
        }

        if (type.equals("102")) {
            //查询活跃用户
            return queryActive(sd, ed);
        }

        if (type.equals("103")) {
            //查询次日留存率
            return queryUntil(sd, ed);
        }
        return null;
    }

    /**
     * 查询次日留存率
     * @param ruleEndTime
     * @param startTime
     * @return
     */
    private Map<String, Object> queryUntil(Long startTime, Long ruleEndTime) {
        //一天的毫秒值
        Long oneDay = 24 * 60 * 60 * 1000L;
        //一年的毫秒值
        Long oneYear = 365 * 24 * 60 * 60 * 1000L;
        //创建集合存储数据
        List<DistributionVo> thisYear = new ArrayList<>();
        List<DistributionVo> lastYear = new ArrayList<>();
        List<Object> idList1 = new ArrayList<>();
        List<Object> idList2 = new ArrayList<>();
        List<Object> idList11 = new ArrayList<>();
        List<Object> idList21 = new ArrayList<>();
        //计算查询的天数
        long time = (ruleEndTime - startTime) / oneDay;
        //查询数据库查询,第一天注册的用户
        for (long i = 0; i < time; i++) {
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            DistributionVo distributionVo = new DistributionVo();
            distributionVo.setTitle(new SimpleDateFormat("MM-dd").format(new Date(startTime + (oneDay * i))));
            //查询第一天注册的用户
            queryWrapper.gt(UserInfo::getCreated, new Date(startTime + (oneDay * i)))
                    .lt(UserInfo::getCreated, new Date(startTime + (oneDay * (i + 1))));
            List<UserInfo> userList = userInfoMapper.selectList(queryWrapper);
            idList1 = CollUtil.getFieldValues(userList, "userId");
            //再去mongodb中查找,第二天访问过的
            List<Log> userList1 = logApi.queryNextDayLeft(startTime + (oneDay * i), startTime + (oneDay * (i + 1)));
            idList2 = CollUtil.getFieldValues(userList1, "userId");
            //次日留存率 （ count/idList1）
            int count = receiveCollectionList(idList1, idList2).size();
            if (idList1.size() == 0) {
                distributionVo.setAmount(0);
            } else {
                distributionVo.setAmount(count / idList1.size());
            }

            thisYear.add(distributionVo);

            //去年的
            LambdaQueryWrapper<UserInfo> queryWrapper1 = new LambdaQueryWrapper<>();
            DistributionVo distributionVo1 = new DistributionVo();
            distributionVo1.setTitle(new SimpleDateFormat("MM-dd").format(new Date(startTime + (oneDay * i) - oneYear)));
            //查询第一天注册的用户
            queryWrapper1.gt(UserInfo::getCreated, new Date(startTime + (oneDay * i) - oneYear))
                    .lt(UserInfo::getCreated, new Date(startTime + (oneDay * (i + 1)) - oneYear));
            idList11 = CollUtil.getFieldValues(userInfoMapper.selectList(queryWrapper1), "userId");
            //再去mongodb中查找,第二天访问过的
            idList21 = CollUtil.getFieldValues(logApi.queryNextDayLeft(startTime + (oneDay * i) - oneYear, startTime + (oneDay * (i + 1)) - oneYear), "userId");
            //次日留存率 = count/idList1.size()
            int count1 = receiveCollectionList(idList11, idList21).size();
            if (idList11.size() == 0) {
                distributionVo1.setAmount(0);
            } else {
                distributionVo1.setAmount(count1 / idList11.size());
            }
            lastYear.add(distributionVo1);
        }

        //封装数据返回
        Map<String, Object> map = new HashMap<>();
        map.put("thisYear", thisYear);
        map.put("lastYear", lastYear);
        return map;
    }

    /**
     * 获取两个ArrayList的交集
     * @param firstArrayList  第一个ArrayList
     * @param secondArrayList 第二个ArrayList
     * @return resultList 交集ArrayList
     */
    public static List<Object> receiveCollectionList(List<Object> firstArrayList, List<Object> secondArrayList) {
        List<Object> resultList = new ArrayList<>();
        LinkedList<Object> result = new LinkedList<>(firstArrayList);// 大集合用linkedlist
        HashSet<Object> othHash = new HashSet<>(secondArrayList);// 小集合用hashset
        Iterator<Object> iter = result.iterator();// 采用Iterator迭代器进行数据的操作
        while (iter.hasNext()) {
            if (!othHash.contains(iter.next())) {
                iter.remove();
            }
        }
        resultList = new ArrayList<Object>(result);
        return resultList;
    }

    /**
     * 查询活跃用户数
     * @param ruleEndTime
     * @param startTime
     * @return
     */
    private Map<String, Object> queryActive(Long startTime, Long ruleEndTime) {
        //一天的毫秒值
        Long oneDay = 24 * 60 * 60 * 1000L;
        //一年的毫秒值
        Long oneYear = 365 * 24 * 60 * 60 * 1000L;
        //创建集合存储数据
        List<DistributionVo> thisYear = new ArrayList<>();
        List<DistributionVo> lastYear = new ArrayList<>();
        //计算查询的天数
        long time = (ruleEndTime - startTime) / oneDay;
        //遍历拿到指定时间段用户数
        for (long i = 0; i < time; i++) {
            DistributionVo distributionVo = new DistributionVo();
            distributionVo.setTitle(new SimpleDateFormat("yyyy-MM-dd").format(new Date(startTime + (oneDay * i))));
            //拿到当前时间段的注册用户
            distributionVo.setAmount(Convert.toInt(logApi.queryActiveUserCount(startTime + (oneDay * i),startTime + (oneDay * (i + 1)))));
            thisYear.add(distributionVo);

            DistributionVo distributionVo1 = new DistributionVo();
            distributionVo1.setTitle(new SimpleDateFormat("yyyy-MM-dd").format(new Date(startTime + (oneDay * i) - oneYear)));
            //拿到去年该时间段时间段的注册用户
            distributionVo1.setAmount(Convert.toInt(logApi.queryActiveUserCount((startTime + (oneDay * i) - oneYear),(startTime + (oneDay * (i + 1)) - oneYear))));
            lastYear.add(distributionVo1);
        }

        //封装数据返回
        Map<String, Object> map = new HashMap<>();
        map.put("thisYear", thisYear);
        map.put("lastYear", lastYear);
        return map;
    }

    /**
     * 查询新增用户数
     * @param startTime
     * @param ruleEndTime
     * @return
     */
    private Map<String, Object> queryCrescent(Long startTime, Long ruleEndTime) {
        //一天的毫秒值
        Long oneDay = 24 * 60 * 60 * 1000L;
        //一年的毫秒值
        Long oneYear = 365 * 24 * 60 * 60 * 1000L;
        //创建集合存储数据
        List<DistributionVo> thisYear = new ArrayList<>();
        List<DistributionVo> lastYear = new ArrayList<>();
        //计算查询的天数
        long time = (ruleEndTime - startTime) / oneDay;
        //遍历拿到指定时间段用户数
        for (long i = 0; i < time; i++) {
            //今年
            DistributionVo distributionVo = new DistributionVo();
            distributionVo.setTitle(new SimpleDateFormat("yyyy-MM-dd").format(new Date(startTime + (oneDay * i))));
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            //拿到当前时间段的注册用户
            queryWrapper.gt(UserInfo::getCreated, new Date(startTime + (oneDay * i)))
                    .lt(UserInfo::getCreated, new Date(startTime + (oneDay * (i + 1))));
            distributionVo.setAmount(userInfoMapper.selectCount(queryWrapper));
            thisYear.add(distributionVo);
            //去年
            DistributionVo distributionVo1 = new DistributionVo();
            distributionVo1.setTitle(new SimpleDateFormat("yyyy-MM-dd").format(new Date(startTime + (oneDay * i) - oneYear)));
            LambdaQueryWrapper<UserInfo> queryWrapper1 = new LambdaQueryWrapper<>();
            //拿到去年该时间段时间段的注册用户
            queryWrapper1.gt(UserInfo::getCreated, new Date(startTime + (oneDay * i) - oneYear))
                    .lt(UserInfo::getCreated, new Date(startTime + (oneDay * (i + 1)) - oneYear));
            distributionVo1.setAmount(userInfoMapper.selectCount(queryWrapper1));
            lastYear.add(distributionVo1);
        }

        //封装数据返回
        Map<String, Object> map = new HashMap<>();
        map.put("thisYear", thisYear);
        map.put("lastYear", lastYear);
        return map;
    }

    /**
     * 概要统计信息
     *
     * @return
     */
    public SummaryVo querySummary() {
        try {
            //获取当天零点时间
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String today = DateUtil.today() + " 00:00:00";
            long zeroTime = simpleDateFormat.parse(today).getTime();
            //当前系统时间
            long nowTime = System.currentTimeMillis();
            //一个月前系统时间
            long monthTime = zeroTime - (30 * 24 * 60 * 60 * 1000L);
            //一周前系统时间
            long weekTime = zeroTime - (7 * 24 * 60 * 60 * 1000L);
            //一天前系统时间
            long dayTime = zeroTime - (24 * 60 * 60 * 1000L);
            //二天前系统时间
            long yesterdayDayTime = zeroTime - (2 * 24 * 60 * 60 * 1000L);

            //查询累计用户
            Integer cumulativeUsersCount = queryCumulativeUsers();

            //查询过去30天活跃用户
            Long activePassMonthCount = logApi.queryActivePassMonth(nowTime, monthTime);

            //查询过去7天活跃数据
            Long activePassWeekCount = logApi.queryActivePassWeek(nowTime, weekTime);

            //今日新增用户数
            Integer newUsersTodayCount = queryNewUsersToday(DateUtil.format(new Date(zeroTime), "yyyy-MM-dd HH:mm:ss"), DateUtil.format(new Date(nowTime), "yyyy-MM-dd HH:mm:ss"));

            //昨日新增用户数
            Integer yesterdayUserCount = queryYesterdayUser(DateUtil.format(new Date(zeroTime), "yyyy-MM-dd HH:mm:ss"), DateUtil.format(new Date(dayTime), "yyyy-MM-dd HH:mm:ss"));

            //今日登录次数
            Long loginTimesTodayCount = logApi.queryLoginTimesToday(zeroTime, nowTime);

            //昨日登录次数
            Long yesterdayCount = logApi.queryYesterday(zeroTime, dayTime);

            //今日活跃用户数
            Long activeUsersToday = logApi.queryActiveUsersToday(zeroTime, nowTime);

            //昨天活跃用户数
            Long yesterdayUsersToday = logApi.queryYesterdayUsersToday(zeroTime, dayTime);

            //前天活跃用户数
            Long yesterdayUsersDateTime = logApi.queryYesterdayUsersDateTime(dayTime, yesterdayDayTime);

            //今日新增用户涨跌率
            Integer newUsersTodayRate = queryNewUsersTodayRate(newUsersTodayCount, yesterdayUserCount);

            //今日登录次数涨跌率
            Integer loginTimesTodayRate = queryLoginTimesTodayRate(loginTimesTodayCount, yesterdayCount);

            //今日活跃用户涨跌率
            Integer activeUsersTodayRate = queryActiveUsersTodayRate(activeUsersToday, yesterdayUsersToday);

            //昨日活跃用户涨跌率
            Integer activeUsersYesterdayRate = queryActiveUsersYesterdayRate(yesterdayUsersDateTime, yesterdayUsersToday);

            //查询过去7天平均时长
            Long useTimePassWeek = queryUseTimePassWeek(zeroTime, weekTime);

            //封装vo对象,响应数据给前端
            SummaryVo summaryVo = new SummaryVo();
            summaryVo.setCumulativeUsers(cumulativeUsersCount);
            summaryVo.setActivePassMonth(Convert.toInt(activePassMonthCount));
            summaryVo.setActivePassWeek(Convert.toInt(activePassWeekCount));
            summaryVo.setNewUsersToday(newUsersTodayCount);
            summaryVo.setNewUsersTodayRate(newUsersTodayRate);
            summaryVo.setLoginTimesToday(Convert.toInt(loginTimesTodayCount));
            summaryVo.setLoginTimesTodayRate(loginTimesTodayRate);
            summaryVo.setActiveUsersToday(Convert.toInt(activeUsersToday));
            summaryVo.setActiveUsersTodayRate(activeUsersTodayRate);
            summaryVo.setActiveUsersYesterday(Convert.toInt(yesterdayUsersToday));
            summaryVo.setActiveUsersYesterdayRate(activeUsersYesterdayRate);
            summaryVo.setUseTimePassWeek(Convert.toLong(useTimePassWeek));
            return summaryVo;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查询过去7天平均使用时间
     *
     * @param zeroTime
     * @param weekTime
     * @return
     */
    private Long queryUseTimePassWeek(long zeroTime, long weekTime) {
        //查询过去7天所有用户使用记录
        List<Log> logList = logApi.queryUser(zeroTime, weekTime);
        List<Long> list = new ArrayList<>();
        //遍历计算使用时长
        for (int i = 0; i < logList.size() - 1; i++) {
            Log log1 = logList.get(logList.size() - i - 1);
            Log log2 = logList.get(logList.size() - i - 2);
            long time = log2.getLogTime() - log1.getLogTime();
            list.add(time);
        }

        //7天所有使用时长相加求平均时长
        Long Time = 0L;
        for (Long aLong : list) {
            Time += aLong;
        }

        //返回结果
        long avgTime = Time / 7 / 1000;
        return avgTime;
    }


    /**
     * 昨日活跃次数涨跌率
     * @param yesterdayUsersDateTime 前日活跃用户数
     * @param yesterdayUsersToday    昨天活跃用户数
     */
    private Integer queryActiveUsersYesterdayRate(Long yesterdayUsersDateTime, Long yesterdayUsersToday) {
        try {
            double result = (yesterdayUsersToday / yesterdayUsersDateTime) - 1;
            long round = Math.round(result * 100);
            return (int) (round);
        } catch (Exception e) {
            //e.printStackTrace();
            return 0;
        }
    }

    /**
     * 今日活跃次数涨跌率
     *
     * @param activeUsersToday    今日活跃用户数
     * @param yesterdayUsersToday 昨天活跃用户数
     */
    private Integer queryActiveUsersTodayRate(Long activeUsersToday, Long yesterdayUsersToday) {
        try {
            double result = (activeUsersToday / yesterdayUsersToday) - 1;
            long round = Math.round(result * 100);
            return (int) (round);
        } catch (Exception e) {
            //throw new BusinessException(ErrorResult.zeroFail());
            return 0;
        }
    }

    /**
     * 今日登录次数涨跌率
     * @param loginTimesTodayCount 今日登录次数
     * @param yesterdayCount       昨日登录次数
     */
    private Integer queryLoginTimesTodayRate(Long loginTimesTodayCount, Long yesterdayCount) {
        try {
            double result = (loginTimesTodayCount / yesterdayCount) - 1;
            long round = Math.round(result * 100);
            return (int) (round);
        } catch (Exception e) {
            //e.printStackTrace();
            return 0;
        }
    }

    /**
     * 今日新增用户涨跌率
     *
     * @param newUsersTodayCount 今日新增用户数
     * @param yesterdayUserCount 昨日新增用户数
     */
    private Integer queryNewUsersTodayRate(Integer newUsersTodayCount, Integer yesterdayUserCount) {
        try {
            double result = (double) (newUsersTodayCount / yesterdayUserCount) - 1;
            long round = Math.round(result * 100);
            return (int) (round);
        } catch (Exception e) {
            //e.printStackTrace();
            return 0;
        }
    }


    /**
     * 新增用户数
     *
     * @param zeroTime 今日0时
     * @param nowTime  当前系统时间
     * @return
     */
    private Integer queryNewUsersToday(String zeroTime, String nowTime) {
        QueryWrapper<UserInfo> query = new QueryWrapper<>();
        query.between("created", zeroTime, nowTime);
        return userInfoMapper.selectCount(query);
    }

    /**
     * 昨日新增用户数
     *
     * @param zeroTime 今日0时
     * @param dayTime  昨日0时
     * @return
     */
    private Integer queryYesterdayUser(String zeroTime, String dayTime) {
        QueryWrapper<UserInfo> query = new QueryWrapper<>();
        query.between("created", dayTime, zeroTime);
        return userInfoMapper.selectCount(query);
    }

    //查询累计用户
    private Integer queryCumulativeUsers() {
        Wrapper<UserInfo> query = new QueryWrapper<>();
        return userInfoMapper.selectCount(query);
    }
}
